/*-
 * Copyright (c) 2006 - 2010 CTPP Team
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 4. Neither the name of the CTPP Team nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      php_ctpp.cpp
 *
 * $CTPP$
 */

#include <new>

#include <CDT.hpp>
#include <CTPP2Exception.hpp>
#include <CTPP2FileSourceLoader.hpp>
#include <CTPP2Parser.hpp>
#include <CTPP2ParserException.hpp>
#include <CTPP2StringOutputCollector.hpp>
#include <CTPP2SyscallFactory.hpp>
#include <CTPP2VM.hpp>
#include <CTPP2VMDebugInfo.hpp>
#include <CTPP2VMDumper.hpp>
#include <CTPP2VMException.hpp>
#include <CTPP2VMStackException.hpp>
#include <CTPP2VMExecutable.hpp>
#include <CTPP2VMMemoryCore.hpp>
#include <CTPP2VMOpcodeCollector.hpp>
#include <CTPP2VMSTDLib.hpp>

#include "php_ctpp.h"
#include "m_ctpp.hpp"

ZEND_DECLARE_MODULE_GLOBALS(ctpp)

zend_class_entry          * ctpp_class_entry;
static zend_object_handlers ctpp_object_handlers;

typedef enum { IS_CTPP } ctpp_object_type;

struct ctpp_object
{
	zend_object      std;
	int              rsrc_id;
	ctpp_object_type type;
	union
	{
		void * data;
	} u;
};

zend_function_entry ctpp_methods[] =
{
	ZEND_ME_MAPPING(CTPP,               ctpp_new,                NULL, 0)
	ZEND_ME_MAPPING(include_dirs,       ctpp_include_dirs,       NULL, 0)
	ZEND_ME_MAPPING(parse_template,     ctpp_parse_template,     NULL, 0)
	ZEND_ME_MAPPING(load_bytecode,      ctpp_load_bytecode,      NULL, 0)
	ZEND_ME_MAPPING(free_bytecode,      ctpp_free_bytecode,      NULL, 0)
	ZEND_ME_MAPPING(save_bytecode,      ctpp_save_bytecode,      NULL, 0)
	ZEND_ME_MAPPING(emit_params,        ctpp_emit_params,        NULL, 0)
	ZEND_ME_MAPPING(reset_params,       ctpp_reset_params,       NULL, 0)
	ZEND_ME_MAPPING(output,             ctpp_output,             NULL, 0)
	ZEND_ME_MAPPING(output_string,      ctpp_output_string,      NULL, 0)
	ZEND_ME_MAPPING(dump_params,        ctpp_dump_params,        NULL, 0)
	ZEND_ME_MAPPING(dump_params_string, ctpp_dump_params_string, NULL, 0)
	ZEND_ME_MAPPING(set_steps_limit,    ctpp_set_steps_limit,    NULL, 0)
	ZEND_ME_MAPPING(get_last_error,     ctpp_get_last_error,     NULL, 0)
	{NULL, NULL, NULL}
};

static int le_ctpp;
static int le_ctpp_bytecode;
static int le_ctpp_bytecode_perm;


/* {{{ CTPP Objects
*/

/*
 * CTPP Virtual machine object
 */
typedef struct CTPPVMObject
{
	/* Syscall factory               */
	CTPP::SyscallFactory        * syscall_factory;
	/* CDT object                    */
	CTPP::CDT                   * cdt;
	/* Include dirs                  */
	STLW::vector<STLW::string>  * include_dirs;
	/* max. size of arguments stack  */
	INT_32                        arg_stack_size;
	/* max. size of code stack       */
	INT_32                        code_stack_size;
	/* max. number of executed steps */
	INT_32                        steps_limit;
	/* max. number of executed steps */
	INT_32                        debug_level;
	/* Error                         */
	M_CTPP::CTPPError             ctpp_error;

} CTPPVMObject;


#define BYTECODE_TEMP_OBJECT 0
#define BYTECODE_PERM_OBJECT 1

//
// CTPP Bytecode object
//
typedef struct CTPPBytecodeObject
{
	// Memory core
	CTPP::VMExecutable   * core;
	// Memory core size
	INT_32                 core_size;
	// Ready-to-run program
	CTPP::VMMemoryCore   * memory_core;
	int                    resource_type;
	int                    active_resource;

} CTPPBytecodeObject;
/* }}} */

zend_function_entry ctpp_functions[] =
{
	PHP_FE(ctpp_new,                NULL)
	PHP_FE(ctpp_destroy,            NULL)
	PHP_FE(ctpp_include_dirs,       NULL)
	PHP_FE(ctpp_parse_template,     NULL)
	PHP_FE(ctpp_load_bytecode,      NULL)
	PHP_FE(ctpp_free_bytecode,      NULL)
	PHP_FE(ctpp_save_bytecode,      NULL)
	PHP_FE(ctpp_emit_params,        NULL)
	PHP_FE(ctpp_reset_params,       NULL)
	PHP_FE(ctpp_output,             NULL)
	PHP_FE(ctpp_output_string,      NULL)
	PHP_FE(ctpp_dump_params,        NULL)
	PHP_FE(ctpp_dump_params_string, NULL)
	PHP_FE(ctpp_set_steps_limit,    NULL)
	PHP_FE(ctpp_get_last_error,     NULL)
	{NULL, NULL, NULL}
};

zend_module_entry ctpp_module_entry = {
	STANDARD_MODULE_HEADER,
	"ctpp",
	ctpp_functions,
	PHP_MINIT(ctpp),
	PHP_MSHUTDOWN(ctpp),
	PHP_RINIT(ctpp),
	PHP_RSHUTDOWN(ctpp),
	PHP_MINFO(ctpp),
	CTPP_VERSION,
	STANDARD_MODULE_PROPERTIES
};

/* {{{ ZEND_GET_MODULE
*/
#ifdef COMPILE_DL_CTPP
extern "C"
{
	ZEND_GET_MODULE(ctpp)
}
#endif
/* }}} */

/* {{{ PHP_INI
*/
PHP_INI_BEGIN()
	STD_PHP_INI_ENTRY("ctpp.arg_stack_size",       "10240",   PHP_INI_ALL, OnUpdateLong, arg_stack_size,  zend_ctpp_globals, ctpp_globals)
	STD_PHP_INI_ENTRY("ctpp.code_stack_size",      "10240",   PHP_INI_ALL, OnUpdateLong, code_stack_size, zend_ctpp_globals, ctpp_globals)
	STD_PHP_INI_ENTRY("ctpp.steps_limit",          "1048576", PHP_INI_ALL, OnUpdateLong, steps_limit,     zend_ctpp_globals, ctpp_globals)
	STD_PHP_INI_ENTRY("ctpp.debug_level",          "0",       PHP_INI_ALL, OnUpdateLong, debug_level,     zend_ctpp_globals, ctpp_globals)
	STD_PHP_INI_ENTRY("ctpp.cache_bytecode",       "1",       PHP_INI_ALL, OnUpdateLong, cache_bytecode,  zend_ctpp_globals, ctpp_globals)
	STD_PHP_INI_ENTRY("ctpp.max_functions",        "1024",    PHP_INI_ALL, OnUpdateLong, max_functions,   zend_ctpp_globals, ctpp_globals)
PHP_INI_END()
/* }}} */

/* Init global variables */
/* {{{ php_ctpp_init_globals
*/
static void php_ctpp_init_globals(zend_ctpp_globals * ctpp_globals)
{
	ctpp_globals -> arg_stack_size  = 10240;
	ctpp_globals -> code_stack_size = 10240;
	ctpp_globals -> steps_limit     = 1048576;
	ctpp_globals -> cache_bytecode  = 1;
	ctpp_globals -> debug_level     = 0;
}
/* }}} */

/* {{{ Forward Decl.
*/
static INT_32 _php_import_value(zval ** pParameter, CTPP::CDT * pCDT);
static CTPPBytecodeObject * _php_create_bytecode_object(CCHAR_P szBytecode, INT_32 iPersistent);
static CTPPBytecodeObject * _php_create_bytecode_object2(CTPP::VMExecutable * pICore, UINT_32 iSize, INT_32 iPersistent);
/* }}} */

/* Free object storage */
/* {{{ ctpp_object_free_storage
*/
static void ctpp_object_free_storage(void *object TSRMLS_DC)
{
	ctpp_object *obj = (ctpp_object *) object;

	zend_hash_destroy(obj -> std.properties);
	FREE_HASHTABLE(obj -> std.properties);

	if (obj -> u.data != NULL) { zend_list_delete(obj -> rsrc_id); }
	efree(obj);
}
/* }}} */

/* Create new object storage */
/* {{{ ctpp_object_new
*/
static void ctpp_object_new(zend_class_entry *class_type, zend_object_handlers *handlers, zend_object_value *retval TSRMLS_DC)
{
	zend_objects_free_object_storage_t free_storage = ctpp_object_free_storage;
	zval *tmp;
	/* Get memory for object */
	ctpp_object *obj = (ctpp_object *) emalloc(sizeof(ctpp_object));
	/* Clear memory */
	memset(obj, 0, sizeof(ctpp_object));
	obj -> std.ce = class_type;

	ALLOC_HASHTABLE(obj -> std.properties);
	zend_hash_init(obj -> std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);

#if PHP_VERSION_ID < 50399
	zend_hash_copy(obj -> std.properties, &(class_type->default_properties), (copy_ctor_func_t) zval_add_ref, (void *)(&tmp), sizeof(zval *));
#else
	object_properties_init(&obj -> std, class_type);
#endif

	retval -> handle = zend_objects_store_put(obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, free_storage, NULL TSRMLS_CC);
	retval -> handlers = handlers;
}
/* }}} */

/* Create new class */
/* {{{ ctpp_object_new_myclass
*/
static zend_object_value ctpp_object_new_myclass(zend_class_entry * pClassEntry TSRMLS_DC)
{
	zend_object_value sReturnValue;
	ctpp_object_new(pClassEntry, &ctpp_object_handlers, &sReturnValue TSRMLS_CC);
return sReturnValue;
}
/* }}} */

/* Destructor */
/* {{{ destroy_ctpp
*/
static ZEND_RSRC_DTOR_FUNC(destroy_ctpp)
{
	if (rsrc -> ptr != NULL)
	{
		CTPPVMObject * pSelf = (CTPPVMObject *)(rsrc -> ptr);

		if (pSelf -> syscall_factory != NULL)
		{
			/* Destroy standard library */
			CTPP::STDLibInitializer::DestroyLibrary(*(pSelf -> syscall_factory));

			delete pSelf -> syscall_factory;
		}

		if (pSelf -> cdt != NULL)          { delete pSelf -> cdt; }
		if (pSelf -> include_dirs != NULL) { delete pSelf -> include_dirs; }
		delete pSelf;

		rsrc -> ptr = NULL;
	}
}
/* }}} */

/* Destructor */
/* {{{ _php_destroy_ctpp_bytecode
*/
static ZEND_RSRC_DTOR_FUNC(_php_destroy_ctpp_bytecode)
{
	if (rsrc -> ptr != NULL)
	{
		CTPPBytecodeObject * pSelf = (CTPPBytecodeObject *)(rsrc -> ptr);

		if (pSelf -> active_resource == 1)
		{
			efree(pSelf -> core);
			efree(pSelf -> memory_core);
		}
		else
		{
			;;
		}
		efree(pSelf);
	}
}
/* }}} */

/* Destructor */
/* {{{ _php_destroy_ctpp_bytecode_perm
*/
static ZEND_RSRC_DTOR_FUNC(_php_destroy_ctpp_bytecode_perm)
{
	if (rsrc -> ptr != NULL)
	{
		CTPPBytecodeObject * pSelf = (CTPPBytecodeObject *)(rsrc -> ptr);

		if (pSelf -> active_resource == 1)
		{
			free(pSelf -> core);
			free(pSelf -> memory_core);
		}
		else
		{
			;;
		}
		free(pSelf);
	}
}
/* }}} */

/* Initialize module */
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(ctpp)
{
	ZEND_INIT_MODULE_GLOBALS(ctpp, php_ctpp_init_globals, NULL);
	REGISTER_INI_ENTRIES();

	zend_class_entry sClassEntry;
	INIT_CLASS_ENTRY(sClassEntry, "CTPP", ctpp_methods);
	// Create object function
	sClassEntry.create_object = ctpp_object_new_myclass;

	ctpp_class_entry = zend_register_internal_class_ex(&sClassEntry, NULL, NULL TSRMLS_CC);
	memcpy(&ctpp_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
	ctpp_object_handlers.clone_obj = NULL;
	ctpp_class_entry -> ce_flags |= ZEND_ACC_FINAL_CLASS;

	/* Register list of global resource destructors
	 *
	 * ZEND_API int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, char *type_name, int module_number);
	 *
	 * ld         - destructor function for normal resource
	 * pld        - destructor function for permanent resource
	 * type_name  - resource name for identification (for var_dump and other functions.
	 */
	le_ctpp = zend_register_list_destructors_ex(destroy_ctpp, NULL, (char *)"CTPP", module_number);

	le_ctpp_bytecode      = zend_register_list_destructors_ex(_php_destroy_ctpp_bytecode,      NULL, (char *)"CTPP_BO", module_number);
	le_ctpp_bytecode_perm = zend_register_list_destructors_ex(NULL, _php_destroy_ctpp_bytecode_perm, (char *)"CTPP_BP", module_number);

return SUCCESS;
}
/* }}} */

/* Unload module */
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION(ctpp)
{
	UNREGISTER_INI_ENTRIES();
	return SUCCESS;
}
/* }}} */

/* New connection */
/* {{{ PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION(ctpp)
{
	return SUCCESS;
}
/* }}} */

/* Shutdown connection */
/* {{{ PHP_RSHUTDOWN_FUNCTION
*/
PHP_RSHUTDOWN_FUNCTION(ctpp)
{
	return SUCCESS;
}
/* }}} */

/* Module Info */
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(ctpp)
{
	php_info_print_table_start();
	php_info_print_table_header(2, "CTPP support", "enabled");
	php_info_print_table_end();
	DISPLAY_INI_ENTRIES();
}
/* }}} */

/* Constructor */
/* {{{ proto resource ctpp_new()
*/
PHP_FUNCTION(ctpp_new)
{
	CTPPVMObject * pCTPPVMObject = NULL;

	try
	{
		using namespace CTPP;
		/* Create CTPP object */
		pCTPPVMObject = new CTPPVMObject;

		pCTPPVMObject -> syscall_factory = NULL;
		pCTPPVMObject -> cdt             = NULL;
		pCTPPVMObject -> include_dirs    = NULL;

		/* Initialize Standard CTPP library */
		INT_32 iMaxFunctions = CTPP_G(max_functions);
		if (iMaxFunctions <= 50)
		{
			iMaxFunctions = 1024;
			php_error(E_WARNING, "%s(): Invalid value max_functions (%u), resetting do default (%d).", get_active_function_name(TSRMLS_C), (unsigned int)CTPP_G(max_functions), (unsigned int)iMaxFunctions);
		}

		/* Load standard library */
		pCTPPVMObject -> syscall_factory = new SyscallFactory(CTPP_G(max_functions));
		STDLibInitializer::InitLibrary(*(pCTPPVMObject -> syscall_factory));

		/* Create object */
		pCTPPVMObject -> cdt             = new CDT(CDT::HASH_VAL);
		pCTPPVMObject -> include_dirs    = new STLW::vector<STLW::string>;
		pCTPPVMObject -> arg_stack_size  = CTPP_G(arg_stack_size);
		pCTPPVMObject -> code_stack_size = CTPP_G(code_stack_size);
		pCTPPVMObject -> steps_limit     = CTPP_G(steps_limit);
		pCTPPVMObject -> debug_level     = CTPP_G(debug_level);
	}
	catch(...)
	{
		php_error(E_ERROR, "%s(): Cannot init module.", get_active_function_name(TSRMLS_C));
	}

	zval * pThis = getThis();
	/* Register resource
	 *
	 * int ZEND_REGISTER_RESOURCE(zval *rsrc_result, void *rsrc_pointer, int rsrc_type);
	 *
	 * rsrc_result   Initialized zval * -container.
	 * rsrc_pointer  our resource.
	 * rsrc_type     - Type which get after registerinf le_myresource
	 */
	int iResourceID = ZEND_REGISTER_RESOURCE(pThis ? NULL : return_value, (void *)pCTPPVMObject, le_ctpp);
	if (pThis)
	{
		ctpp_object * pStoredObject = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);
		pStoredObject -> u.data  = (void *)pCTPPVMObject;
		pStoredObject -> rsrc_id = iResourceID;
		pStoredObject -> type    = IS_CTPP;
	}
}
/* }}} */

/* Destructor */
/* {{{ proto ctpp_destroy()
*/
PHP_FUNCTION(ctpp_destroy)
{
	zval * pResource;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"r", &pResource) == FAILURE) { RETURN_FALSE; }

	if (zend_list_delete(Z_RESVAL_P(pResource)) == FAILURE) { RETURN_FALSE; }

RETURN_TRUE;
}
/* }}} */

/* List of include dirs */
/* {{{ proto ctpp_include_dirs()
*/
PHP_FUNCTION(ctpp_include_dirs)
{
	CTPPVMObject * pThisObject = NULL;

	zval         * pRParam;
	zval * pThis = getThis();

	if (pThis != NULL)
	{
		if(ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
		ctpp_object * obj = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"a", &pRParam) == FAILURE) { RETURN_FALSE; }

		pThisObject = (CTPPVMObject *)(obj -> u.data);
	}
	else
	{
		if(ZEND_NUM_ARGS() != 2) { WRONG_PARAM_COUNT; }
		zval * pRThis;
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"ra", &pRThis, &pRParam) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE(pThisObject, CTPPVMObject *, &pRThis, -1, (char *)"CTPP", le_ctpp);
	}

	try
	{
		/* Clear template */
		{ STLW::vector<STLW::string> vTMP; pThisObject -> include_dirs -> swap(vTMP); }

		zval ** dataset;
		HashPosition sPos;
		zend_hash_internal_pointer_reset_ex(pRParam -> value.ht, &sPos);
		while(zend_hash_get_current_data_ex(pRParam -> value.ht, (void **) &dataset, &sPos) == SUCCESS)
		{
			if ((*dataset) -> type == IS_STRING)
			{
				if ((*dataset) -> value.str.val == NULL || *((*dataset) -> value.str.val) == '\0' || php_check_open_basedir((*dataset) -> value.str.val TSRMLS_CC))
				{
					php_error(E_WARNING, "%s(): open_basedir restriction.", get_active_function_name(TSRMLS_C));
				}
				else
				{
					pThisObject -> include_dirs -> push_back(STLW::string((*dataset) -> value.str.val, (*dataset) -> value.str.len));
				}
			}
			else
			{
				php_error(E_WARNING, "%s(): Include path MUST be a string.", get_active_function_name(TSRMLS_C));
			}

			zend_hash_move_forward_ex(pRParam -> value.ht, &sPos);
		}
		RETURN_TRUE;
	}
	catch (std::exception &e) { php_error(E_WARNING, "%s(): %s",    get_active_function_name(TSRMLS_C), e.what()); }
	catch (...)               { php_error(E_WARNING, "%s(): Ouch!", get_active_function_name(TSRMLS_C));           }

RETURN_FALSE;
}

/* Parse template bytecode */
/* {{{ proto ctpp_parse_template()
*/
PHP_FUNCTION(ctpp_parse_template)
{
	CTPPVMObject * pThisObject      = NULL;

	char         * szTemplate       = NULL;
	int            uiTemplateLength = 0;

	zval * pThis = getThis();
	if (pThis != NULL)
	{
		if(ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
		ctpp_object * obj = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"s", &szTemplate, &uiTemplateLength) == FAILURE) { RETURN_FALSE; }

		pThisObject = (CTPPVMObject *)(obj -> u.data);
	}
	else
	{
		if(ZEND_NUM_ARGS() != 2) { WRONG_PARAM_COUNT; }
		zval * pRThis;
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"rs", &pRThis, &szTemplate, &uiTemplateLength) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE(pThisObject, CTPPVMObject *, &pRThis, -1, (char *)"CTPP", le_ctpp);
	}

	using namespace CTPP;
	using namespace M_CTPP;


	UINT_32        iSize = 0;
	VMExecutable * pCore = NULL;

	int    iHashedBytecodeLength = 0;
	char * szHashedBytecode      = NULL;

	/* Load & parse template */
	INT_32 iOK = -1;
	try
	{
		/* Load template */
		CTPP2FileSourceLoader oSourceLoader;
		oSourceLoader.SetIncludeDirs(*(pThisObject -> include_dirs));
		oSourceLoader.LoadTemplate(szTemplate);

		/* Check the basedir */
		CCHAR_P szRealTemplateName = oSourceLoader.GetTemplateName();
		if (szRealTemplateName == NULL || *szRealTemplateName == '\0' || php_check_open_basedir(szRealTemplateName TSRMLS_CC))
		{
			pThisObject -> ctpp_error = CTPPError(szTemplate, STLW::string("Invalid template file \"") + szRealTemplateName + "\", (given \"" + szTemplate + "\")", CTPP_VM_ERROR | CTPP_UNIX_ERROR, 0, 0, 0);

			php_error(E_WARNING, "%s(): Invalid template file \"%s\" (given \"%s\").", get_active_function_name(TSRMLS_C), szRealTemplateName, szTemplate);
			RETURN_FALSE;
		}

		iHashedBytecodeLength = sizeof("ctpp2_") - 1 + strlen(szRealTemplateName);
		szHashedBytecode      = (char *)emalloc(iHashedBytecodeLength + 1);
		sprintf(szHashedBytecode, "ctpp2_%s", szRealTemplateName);

		VMOpcodeCollector  oVMOpcodeCollector;
		StaticText         oSyscalls;
		StaticData         oStaticData;
		StaticText         oStaticText;
		CTPP::HashTable    oHashTable;

		/* Create template compiler */
		CTPP2Compiler oCompiler(oVMOpcodeCollector, oSyscalls, oStaticData, oStaticText, oHashTable);

		/* Create template parser   */
		CTPP2Parser oCTPP2Parser(&oSourceLoader, &oCompiler, szRealTemplateName);

		/* Compile template */
		oCTPP2Parser.Compile();

		/* Get program core */
		UINT_32 iCodeSize = 0;
		const VMInstruction * oVMInstruction = oVMOpcodeCollector.GetCode(iCodeSize);
		/* Dump program */
		VMDumper oDumper(iCodeSize, oVMInstruction, oSyscalls, oStaticData, oStaticText, oHashTable);

		const VMExecutable * pTMPCore = oDumper.GetExecutable(iSize);

		// TBD: mutex for thread-safety
		size_t iMemoryLimit = PG(memory_limit);
		size_t iMemoryUsage = zend_memory_usage(0 TSRMLS_CC);
		if ((iMemoryUsage + iSize + 4096) > iMemoryLimit)
		{
			efree(szHashedBytecode);
			RETURN_FALSE;
		}

		/* Try to allocate memory, if failed, it is safe of memory leaks */
		pCore = (VMExecutable *)emalloc(iSize);
		/* All okay, read data and check type of loaded file */
		memcpy(pCore, pTMPCore, iSize);

		iOK = 0;
	}
	catch (CTPPParserSyntaxError        & e)
	{
		pThisObject -> ctpp_error = CTPPError(szTemplate, e.what(), CTPP_COMPILER_ERROR | CTPP_SYNTAX_ERROR, e.GetLine(), e.GetLinePos(), 0);
	}
	catch (CTPPParserOperatorsMismatch  & e)
	{
		pThisObject -> ctpp_error = CTPPError(szTemplate, std::string("Expected ") + e.Expected() + ", but found " + e.Found(), CTPP_COMPILER_ERROR | CTPP_SYNTAX_ERROR, e.GetLine(), e.GetLinePos(), 0);
	}
	catch (CTPPUnixException            & e) { pThisObject -> ctpp_error = CTPPError(szTemplate, e.what(), CTPP_COMPILER_ERROR | CTPP_UNIX_ERROR,                    0, 0, 0); }
	catch (CDTRangeException            & e) { pThisObject -> ctpp_error = CTPPError(szTemplate, e.what(), CTPP_COMPILER_ERROR | CTPP_RANGE_ERROR,                   0, 0, 0); }
	catch (CDTAccessException           & e) { pThisObject -> ctpp_error = CTPPError(szTemplate, e.what(), CTPP_COMPILER_ERROR | CTPP_ACCESS_ERROR,                  0, 0, 0); }
	catch (CDTTypeCastException         & e) { pThisObject -> ctpp_error = CTPPError(szTemplate, e.what(), CTPP_COMPILER_ERROR | CTPP_TYPE_CAST_ERROR,               0, 0, 0); }
	catch (CTPPLogicError               & e) { pThisObject -> ctpp_error = CTPPError(szTemplate, e.what(), CTPP_COMPILER_ERROR | CTPP_LOGIC_ERROR,                   0, 0, 0); }
	catch (CTPPException                & e) { pThisObject -> ctpp_error = CTPPError(szTemplate, e.what(), CTPP_COMPILER_ERROR | CTPP_UNKNOWN_ERROR,                 0, 0, 0); }
	catch (std::exception               & e) { pThisObject -> ctpp_error = CTPPError(szTemplate, e.what(), CTPP_COMPILER_ERROR | STL_UNKNOWN_ERROR,                  0, 0, 0); }
	catch (...)                              { pThisObject -> ctpp_error = CTPPError(szTemplate, "Unknown Error", CTPP_COMPILER_ERROR | STL_UNKNOWN_ERROR,           0, 0, 0); }

	// Error occured
	if (iOK == -1)
	{
		php_error(E_WARNING, "%s(): %s (error code 0x%08X), template %s, line %u, pos %u", get_active_function_name(TSRMLS_C),
		                                                 pThisObject -> ctpp_error.error_descr.c_str(),
		                                                 pThisObject -> ctpp_error.error_code,
		                                                 pThisObject -> ctpp_error.template_name.c_str(),
		                                                 pThisObject -> ctpp_error.line,
		                                                 pThisObject -> ctpp_error.pos);
		RETURN_FALSE;
	}

	/* So, we can create bytecode object in same way as in method ctpp_load_bytecode */
	CTPPBytecodeObject * pBytecode = NULL;
	if (CTPP_G(cache_bytecode) == 1)
	{
//fprintf(stderr, "PERSISTENT\n");
		zend_rsrc_list_entry *le;

		if (zend_hash_find(&EG(persistent_list), szHashedBytecode, iHashedBytecodeLength + 1, (void **) &le) == FAILURE)
		{
//fprintf(stderr, "Create NEW: ");
			zend_rsrc_list_entry new_le;

			/* Create resource */
			pBytecode = _php_create_bytecode_object2(pCore, iSize, BYTECODE_PERM_OBJECT);

			/* Error? */
			if (pBytecode == NULL)
			{
				efree(szHashedBytecode);
//fprintf(stderr, "FAILED\n");
				RETURN_FALSE;
			}

			/* Okay, set active flag   */
			pBytecode -> active_resource = 1;

			/* Try to register in hash */
			Z_TYPE(new_le) = le_ctpp_bytecode_perm;
			new_le.ptr = pBytecode;
			if (zend_hash_update(&EG(persistent_list), szHashedBytecode, iHashedBytecodeLength + 1, (void *) &new_le, sizeof(zend_rsrc_list_entry), NULL) == FAILURE)
			{
				free(pBytecode -> memory_core);
				free(pBytecode -> core);
				free(pBytecode);
				efree(szHashedBytecode);
//fprintf(stderr, "FAILED\n");
				RETURN_FALSE;
			}
//fprintf(stderr, "OK(%p)\n", pBytecode);
		}
		else
		{
//fprintf(stderr, "PERS Fetch from cache: ");
			/* Compare type of resource */
			if (Z_TYPE_P(le) != le_ctpp_bytecode_perm)
			{
//fprintf(stderr, "FAILED (wrong type)\n");
				efree(pCore);
				efree(szHashedBytecode);
				RETURN_FALSE;
			}

			CTPPBytecodeObject * pTMPBytecode = _php_create_bytecode_object2(pCore, iSize, BYTECODE_PERM_OBJECT);
			if (pTMPBytecode == NULL)
			{
//fprintf(stderr, "FAILED (can't create again)\n");
				efree(szHashedBytecode);
				RETURN_FALSE;
			}

			pBytecode = (CTPPBytecodeObject *)(le -> ptr);
			/*
			   Check resource.
			   If resource active, inactivate it
			*/
			if (pBytecode -> active_resource == 1)
			{
				free(pBytecode -> memory_core);
				free(pBytecode -> core);
			}
			pBytecode -> memory_core     = pTMPBytecode -> memory_core;
			pBytecode -> core            = pTMPBytecode -> core;
			pBytecode -> active_resource = 1;

			free(pTMPBytecode);
//fprintf(stderr, "OK (%p)\n", pBytecode);
		}
		ZEND_REGISTER_RESOURCE(return_value, pBytecode, le_ctpp_bytecode_perm);
	}
	else
	{
//fprintf(stderr, "TEMPOPRARY\n");
		zend_rsrc_list_entry *index_ptr, new_index_ptr;

		if (zend_hash_find(&EG(regular_list), szHashedBytecode, iHashedBytecodeLength + 1, (void **) &index_ptr) == SUCCESS)
		{
//fprintf(stderr, "TEMP Fetch from cache: ");
			int  type;
			long link;
			void *ptr;

			if (Z_TYPE_P(index_ptr) != le_index_ptr)
			{
				efree(szHashedBytecode);
				RETURN_FALSE;
			}
			link = (long) index_ptr -> ptr;
			ptr = zend_list_find(link,&type);
			if (ptr && (type == le_ctpp_bytecode || type == le_ctpp_bytecode_perm))
			{
				CTPPBytecodeObject * pTMPBytecode = _php_create_bytecode_object2(pCore, iSize, BYTECODE_TEMP_OBJECT);
				if (pTMPBytecode == NULL)
				{
//fprintf(stderr, "FAILED (can't create again)\n");
					efree(szHashedBytecode);
					RETURN_FALSE;
				}

				pBytecode = (CTPPBytecodeObject *)(ptr);
				/*
				   Check resource.
				   If resource active, inactivate it
				*/
				if (pBytecode -> active_resource == 1)
				{
					efree(pBytecode -> memory_core);
					efree(pBytecode -> core);
				}
				pBytecode -> memory_core     = pTMPBytecode -> memory_core;
				pBytecode -> core            = pTMPBytecode -> core;
				pBytecode -> active_resource = 1;
				efree(pTMPBytecode);

				zend_list_addref(link);
				Z_LVAL_P(return_value) = link;
				Z_TYPE_P(return_value) = IS_RESOURCE;
				efree(szHashedBytecode);
//fprintf(stderr, "OK (%p)\n", link);
				return;
			}
			else
			{
//fprintf(stderr, "ERROR (invalid entry)\n");
				zend_hash_del(&EG(regular_list), szHashedBytecode, iHashedBytecodeLength + 1);
			}
		}
//fprintf(stderr, "Create NEW temp: ");
		/* Create resource */
		pBytecode = _php_create_bytecode_object2(pCore, iSize, BYTECODE_TEMP_OBJECT);

		/* Error? */
		if (pBytecode == NULL)
		{
			efree(szHashedBytecode);
//fprintf(stderr, "FAILED\n");
			RETURN_FALSE;
		}

		/* Okay, set active flag */
		pBytecode -> active_resource = 1;

		/* Register resource     */
		ZEND_REGISTER_RESOURCE(return_value, pBytecode, le_ctpp_bytecode);

		/* Try to register in hash */
		new_index_ptr.ptr = (void *) Z_LVAL_P(return_value);
		Z_TYPE(new_index_ptr) = le_index_ptr;
		if (zend_hash_update(&EG(regular_list), szHashedBytecode, iHashedBytecodeLength + 1,(void *) &new_index_ptr, sizeof(zend_rsrc_list_entry), NULL)==FAILURE)
		{
			efree(pBytecode -> memory_core);
			efree(pBytecode -> core);
			efree(pBytecode);
			efree(szHashedBytecode);
//fprintf(stderr, "FAILED\n");
			RETURN_FALSE;
		}
//fprintf(stderr, "OK (%p)\n", pBytecode);
	}

	efree(szHashedBytecode);
}
/* }}} */

/* Load bytecode */
/* {{{ proto resource ctpp_load_bytecode(filename)
*/
PHP_FUNCTION(ctpp_load_bytecode)
{
	CTPPVMObject * pThisObject      = NULL;

	char         * szBytecode       = NULL;
	int            uiBytecodeLength = 0;
	zval         * pThis = getThis();

	if (pThis != NULL)
	{
		if(ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
		ctpp_object * obj = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"s", &szBytecode, &uiBytecodeLength) == FAILURE) { RETURN_FALSE; }

		pThisObject = (CTPPVMObject *)(obj -> u.data);
	}
	else
	{
		if(ZEND_NUM_ARGS() != 2) { WRONG_PARAM_COUNT; }
		zval * pRThis;
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"rs", &pRThis, &szBytecode, &uiBytecodeLength) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE(pThisObject, CTPPVMObject *, &pRThis, -1, (char *)"CTPP", le_ctpp);
	}

	/* Check the basedir */
	if (szBytecode == NULL || *szBytecode == '\0' || php_check_open_basedir(szBytecode TSRMLS_CC)) { RETURN_FALSE; }

	int    iHashedBytecodeLength = sizeof("ctpp2_") - 1 + uiBytecodeLength;
	char * szHashedBytecode      = (char *)emalloc(iHashedBytecodeLength + 1);

	sprintf(szHashedBytecode, "ctpp2_%s", szBytecode);

	CTPPBytecodeObject * pBytecode = NULL;
	if (CTPP_G(cache_bytecode) == 1)
	{
//fprintf(stderr, "PERSISTENT\n");
		zend_rsrc_list_entry *le;

		if (zend_hash_find(&EG(persistent_list), szHashedBytecode, iHashedBytecodeLength + 1, (void **) &le) == FAILURE)
		{
//fprintf(stderr, "Create NEW: ");
			zend_rsrc_list_entry new_le;

			/* Create resource */
			pBytecode = _php_create_bytecode_object(szBytecode, BYTECODE_PERM_OBJECT);

			/* Error? */
			if (pBytecode == NULL)
			{
				efree(szHashedBytecode);
//fprintf(stderr, "FAILED\n");
				RETURN_FALSE;
			}

			/* Okay, set active flag   */
			pBytecode -> active_resource = 1;

			/* Try to register in hash */
			Z_TYPE(new_le) = le_ctpp_bytecode_perm;
			new_le.ptr = pBytecode;
			if (zend_hash_update(&EG(persistent_list), szHashedBytecode, iHashedBytecodeLength + 1, (void *) &new_le, sizeof(zend_rsrc_list_entry), NULL) == FAILURE)
			{
				free(pBytecode -> memory_core);
				free(pBytecode -> core);
				free(pBytecode);
				efree(szHashedBytecode);
//fprintf(stderr, "FAILED\n");
				RETURN_FALSE;
			}
//fprintf(stderr, "OK(%p)\n", pBytecode);
		}
		else
		{
//fprintf(stderr, "PERS Fetch from cache: ");
			/* Compare type of resource */
			if (Z_TYPE_P(le) != le_ctpp_bytecode_perm)
			{
//fprintf(stderr, "FAILED (wrong type)\n");
				efree(szHashedBytecode);
				RETURN_FALSE;
			}

			pBytecode = (CTPPBytecodeObject *)(le -> ptr);
			/*
			   Check resource.
			   If resource is inactive, try to create it
			*/
			if (pBytecode -> active_resource != 1)
			{
//fprintf(stderr, "FAILED (resource inactive)");
				/* Create resource */
				CTPPBytecodeObject * pTMPBytecode = _php_create_bytecode_object(szBytecode, BYTECODE_PERM_OBJECT);
				if (pTMPBytecode == NULL)
				{
//fprintf(stderr, "FAILED (can't create again)\n");
					efree(szHashedBytecode);
					RETURN_FALSE;
				}
				pBytecode -> active_resource = 1;
				pBytecode -> memory_core = pTMPBytecode -> memory_core;
				pBytecode -> core        = pTMPBytecode -> core;
				free(pTMPBytecode);
//fprintf(stderr, " RECREATED ");
			}
//fprintf(stderr, "OK (%p)\n", pBytecode);
		}
		ZEND_REGISTER_RESOURCE(return_value, pBytecode, le_ctpp_bytecode_perm);
	}
	else
	{
//fprintf(stderr, "TEMPOPRARY\n");
		zend_rsrc_list_entry *index_ptr, new_index_ptr;

		if (zend_hash_find(&EG(regular_list), szHashedBytecode, iHashedBytecodeLength + 1, (void **) &index_ptr) == SUCCESS)
		{
//fprintf(stderr, "TEMP Fetch from cache: ");
			int  type;
			long link;
			void *ptr;

			if (Z_TYPE_P(index_ptr) != le_index_ptr)
			{
				efree(szHashedBytecode);
				RETURN_FALSE;
			}
			link = (long) index_ptr -> ptr;
			ptr = zend_list_find(link,&type);
			if (ptr && (type == le_ctpp_bytecode || type == le_ctpp_bytecode_perm))
			{
				zend_list_addref(link);
				Z_LVAL_P(return_value) = link;
				Z_TYPE_P(return_value) = IS_RESOURCE;
				efree(szHashedBytecode);
//fprintf(stderr, "OK (%p) %p\n", link, ptr);
				return;
			}
			else
			{
//fprintf(stderr, "ERROR (invalid entry)\n");
				zend_hash_del(&EG(regular_list), szHashedBytecode, iHashedBytecodeLength + 1);
			}
		}
//fprintf(stderr, "Create NEW temp: ");
		/* Create resource */
		pBytecode = _php_create_bytecode_object(szBytecode, BYTECODE_TEMP_OBJECT);

		/* Error? */
		if (pBytecode == NULL)
		{
			efree(szHashedBytecode);
//fprintf(stderr, "FAILED\n");
			RETURN_FALSE;
		}

		/* Okay, set active flag */
		pBytecode -> active_resource = 1;

		/* Register resource     */
		ZEND_REGISTER_RESOURCE(return_value, pBytecode, le_ctpp_bytecode);

		/* Try to register in hash */
		new_index_ptr.ptr = (void *) Z_LVAL_P(return_value);
		Z_TYPE(new_index_ptr) = le_index_ptr;
		if (zend_hash_update(&EG(regular_list), szHashedBytecode, iHashedBytecodeLength + 1,(void *) &new_index_ptr, sizeof(zend_rsrc_list_entry), NULL)==FAILURE)
		{
			efree(pBytecode -> memory_core);
			efree(pBytecode -> core);
			efree(pBytecode);
			efree(szHashedBytecode);
//fprintf(stderr, "FAILED\n");
			RETURN_FALSE;
		}
//fprintf(stderr, "OK (%p)\n", pBytecode);
	}

	efree(szHashedBytecode);
}

/* Free bytecode */
/* {{{ proto ctpp_free_bytecode()
*/
PHP_FUNCTION(ctpp_free_bytecode)
{
	zval                * pRParam;
	CTPPVMObject        * pThisObject = NULL;
	CTPPBytecodeObject  * pBytecode   = NULL;

	zval * pThis = getThis();
	if (pThis != NULL)
	{
		if(ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
		ctpp_object * obj = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);

		pThisObject = (CTPPVMObject *)(obj -> u.data);

		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"r", &pRParam) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE2(pBytecode, CTPPBytecodeObject *, &pRParam, -1, (char *)"CTPP_BP", le_ctpp_bytecode_perm, le_ctpp_bytecode);
	}
	else
	{
		if(ZEND_NUM_ARGS() != 2) { WRONG_PARAM_COUNT; }
		zval * pRThis;
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"rr", &pRThis, &pRParam) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE(pThisObject, CTPPVMObject *, &pRThis, -1, (char *)"CTPP", le_ctpp);
		ZEND_FETCH_RESOURCE2(pBytecode, CTPPBytecodeObject *, &pRParam, -1, (char *)"CTPP_BP", le_ctpp_bytecode_perm, le_ctpp_bytecode);
	}

	if (pBytecode -> active_resource == 1)
	{
		if (pBytecode -> resource_type == BYTECODE_PERM_OBJECT)
		{
			free(pBytecode -> memory_core);
			free(pBytecode -> core);
		}
		else
		{
			efree(pBytecode -> memory_core);
			efree(pBytecode -> core);
		}
		pBytecode -> memory_core = NULL;
		pBytecode -> core        = NULL;
	}
	else
	{
		php_error(E_WARNING, "%s(): invalid resource", get_active_function_name(TSRMLS_C));
	}
	pBytecode -> active_resource = 0;

	if (zend_list_delete(Z_LVAL_PP(&pRParam)) == SUCCESS) { RETURN_TRUE; }

RETURN_FALSE;
}
/* }}} */

/* Free bytecode */
/* {{{ proto ctpp_save_bytecode()
*/
PHP_FUNCTION(ctpp_save_bytecode)
{
	zval                * pRParam;
	CTPPVMObject        * pThisObject      = NULL;
	CTPPBytecodeObject  * pBytecodeObject  = NULL;
	char                * szBytecode       = NULL;
	int                   uiBytecodeLength = 0;

	zval * pThis = getThis();
	if (pThis != NULL)
	{
		if(ZEND_NUM_ARGS() != 2) { WRONG_PARAM_COUNT; }
		ctpp_object * obj = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"rs", &pRParam, &szBytecode, &uiBytecodeLength) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE2(pBytecodeObject, CTPPBytecodeObject *, &pRParam, -1, (char *)"CTPP_BP", le_ctpp_bytecode_perm, le_ctpp_bytecode);

		/* Not need yet */
		pThisObject = (CTPPVMObject *)(obj -> u.data);
	}
	else
	{
		if(ZEND_NUM_ARGS() != 3) { WRONG_PARAM_COUNT; }
		zval * pRThis;
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"rrs", &pRThis, &pRParam, &szBytecode, &uiBytecodeLength) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE2(pBytecodeObject, CTPPBytecodeObject *, &pRParam, -1, (char *)"CTPP_BP", le_ctpp_bytecode_perm, le_ctpp_bytecode);
	}

	/* Check the basedir */
	if (szBytecode == NULL || *szBytecode == '\0' || php_check_open_basedir(szBytecode TSRMLS_CC)) { RETURN_FALSE; }

	/* Open file */
	FILE * FW = fopen(szBytecode, "w");
	if (FW == NULL)
	{
		php_error(E_WARNING, "%s(): fopen(\"%s\", \"w\") failed: %s (%d)", get_active_function_name(TSRMLS_C), szBytecode, strerror(errno), errno);
		RETURN_FALSE;
	}

	try
	{
		using namespace CTPP;
		VMDumper oDumper(*(pBytecodeObject -> memory_core));

		UINT_32 iSize = 0;
		const VMExecutable * aProgramCore = oDumper.GetExecutable(iSize);

		/* Write data */
		if (fwrite((void *)aProgramCore, iSize, 1, FW) != 1)
		{
			php_error(E_WARNING, "%s(): fwrite(\"%s\", \"w\") failed: %s (%d)", get_active_function_name(TSRMLS_C), szBytecode, strerror(errno), errno);
			fclose(FW);
			RETURN_FALSE;
		}

		/* All done */
		fclose(FW);
	}
	catch(...)
	{
		fclose(FW);
	}
RETURN_TRUE;
}
/* }}} */

/* Add parameters */
/* {{{ proto ctpp_emit_params(array)
*/
PHP_FUNCTION(ctpp_emit_params)
{
	using namespace CTPP;
	using namespace M_CTPP;

	CTPPVMObject  * pThisObject = NULL;
	zval          * pRParam;
	zval          * pThis = getThis();

	if (pThis != NULL)
	{
		if(ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
		ctpp_object * obj = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"a", &pRParam) == FAILURE) { RETURN_FALSE; }

		pThisObject = (CTPPVMObject *)(obj -> u.data);
	}
	else
	{
		if(ZEND_NUM_ARGS() != 2) { WRONG_PARAM_COUNT; }
		zval * pRThis;
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"ra", &pRThis, &pRParam) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE(pThisObject, CTPPVMObject *, &pRThis, -1, (char *)"CTPP", le_ctpp);
	}

	/* Import data */
	if (_php_import_value(&pRParam, pThisObject -> cdt) == -1)
	{
		pThisObject -> ctpp_error = CTPPError("", "Can't import data", CTPP_DATA_ERROR | CTPP_LOGIC_ERROR);

		php_error(E_WARNING, "CTPP: Can't import data");
		RETURN_FALSE;
	}

#ifdef CTPP_DEBUG
	/* Write data to output */
	M_CTPP::PHPOutputCollector oOutputCollector;
	const STLW::string sTMP = pThisObject -> cdt -> RecursiveDump();
	oOutputCollector.Collect(sTMP.c_str(), sTMP.size());
#endif
	RETURN_TRUE;
}
/* }}} */

/* Reset parameters */
/* {{{ proto ctpp_reset_params()
*/
PHP_FUNCTION(ctpp_reset_params)
{
	using namespace CTPP;
	using namespace M_CTPP;

	CTPPVMObject  * pThisObject = NULL;
	zval          * pThis = getThis();

	if (pThis != NULL)
	{
		if(ZEND_NUM_ARGS() != 0) { WRONG_PARAM_COUNT; }
		ctpp_object * obj = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);

		pThisObject = (CTPPVMObject *)(obj -> u.data);
	}
	else
	{
		if(ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
		zval * pRThis;
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"r", &pRThis) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE(pThisObject, CTPPVMObject *, &pRThis, -1, (char *)"CTPP", le_ctpp);
	}

	/* Reset data */
	INT_32 iOK = -1;
	try
	{
		pThisObject -> cdt -> operator=(CDT(CDT::HASH_VAL));
		iOK = 0;
	}
	catch (...)
	{
		pThisObject -> ctpp_error = CTPPError("", "Can't reset parameters", CTPP_DATA_ERROR | STL_UNKNOWN_ERROR);
	}

	// Error occured
	if (iOK == -1)
	{
		php_error(E_WARNING, "%s(): Can't reset parameters: %s (%d)", get_active_function_name(TSRMLS_C),
		                                                              pThisObject -> ctpp_error.error_descr.c_str(),
		                                                              pThisObject -> ctpp_error.error_code);
		RETURN_FALSE;
	}

RETURN_TRUE;
}
/* }}} */

/* Print data to standard output */
/* {{{ proto ctpp_output()
*/
PHP_FUNCTION(ctpp_output)
{
	using namespace CTPP;
	using namespace M_CTPP;

	CTPPVMObject        * pThisObject     = NULL;
	CTPPBytecodeObject  * pBytecodeObject = NULL;
	zval * pRParam;

	zval * pThis = getThis();
	if (pThis != NULL)
	{
		if(ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
		ctpp_object * obj = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);

		pThisObject = (CTPPVMObject *)(obj -> u.data);

		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"r", &pRParam) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE2(pBytecodeObject, CTPPBytecodeObject *, &pRParam, -1, (char *)"CTPP_BP", le_ctpp_bytecode_perm, le_ctpp_bytecode);
	}
	else
	{
		if(ZEND_NUM_ARGS() != 2) { WRONG_PARAM_COUNT; }
		zval * pRThis;
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"rr", &pRThis, &pRParam) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE(pThisObject, CTPPVMObject *, &pRThis, -1, (char *)"CTPP", le_ctpp);
		ZEND_FETCH_RESOURCE2(pBytecodeObject, CTPPBytecodeObject *, &pRParam, -1, (char *)"CTPP_BP", le_ctpp_bytecode_perm, le_ctpp_bytecode);
	}

	// Run virtual machine
	INT_32      iOK = -1;
	UINT_32     iIP = 0;
	try
	{
		/* Logger */
		PHPLogger oLogger;
		/* Output data collector */
		PHPOutputCollector oOutputCollector;
		/* Get program core */
		const VMMemoryCore * pVMMemoryCore = pBytecodeObject -> memory_core;

		/* Run program */
		VM oVM(  pThisObject -> syscall_factory,
		         pThisObject -> arg_stack_size,
		         pThisObject -> code_stack_size,
		         pThisObject -> steps_limit,
		         pThisObject -> debug_level);


		oVM.Init(pVMMemoryCore, &oOutputCollector, &oLogger);
		oVM.Run(pVMMemoryCore, &oOutputCollector, iIP, *(pThisObject -> cdt), &oLogger);
		iOK = 0;
	}
	catch (ZeroDivision           & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_ZERO_DIVISION_ERROR,           VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (ExecutionLimitReached  & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_EXECUTION_LIMIT_REACHED_ERROR, VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (CodeSegmentOverrun     & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_CODE_SEGMENT_OVERRUN_ERROR,    VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (InvalidSyscall         & e)
	{
		if (e.GetIP() != 0)
		{
			pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_INVALID_SYSCALL_ERROR,         VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP());
		}
		else
		{
			pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), std::string("Unsupported syscall: \"") + e.what() + "\"", CTPP_VM_ERROR | CTPP_INVALID_SYSCALL_ERROR,         VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP());
		}
	}
	catch (IllegalOpcode          & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_ILLEGAL_OPCODE_ERROR,          VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (StackOverflow          & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_STACK_OVERFLOW_ERROR,          VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (StackUnderflow         & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_STACK_UNDERFLOW_ERROR,         VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (VMException            & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_VM_GENERIC_ERROR,              VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (CTPPUnixException      & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | CTPP_UNIX_ERROR,                    0, 0, iIP); }
	catch (CDTRangeException      & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | CTPP_RANGE_ERROR,                   0, 0, iIP); }
	catch (CDTAccessException     & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | CTPP_ACCESS_ERROR,                  0, 0, iIP); }
	catch (CDTTypeCastException   & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | CTPP_TYPE_CAST_ERROR,               0, 0, iIP); }
	catch (CTPPLogicError         & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | CTPP_LOGIC_ERROR,                   0, 0, iIP); }
	catch (CTPPException          & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | CTPP_UNKNOWN_ERROR,                 0, 0, iIP); }
	catch (std::exception         & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | STL_UNKNOWN_ERROR,                  0, 0, iIP); }
	catch (...)                        { pThisObject -> ctpp_error = CTPPError("", "Unknown Error", CTPP_VM_ERROR | STL_UNKNOWN_ERROR,           0, 0, iIP); }

	// Error occured
	if (iOK == -1)
	{
		php_error(E_WARNING, "%s(): %s (error code 0x%08X); IP: 0x%08X", get_active_function_name(TSRMLS_C),
		                                                 pThisObject -> ctpp_error.error_descr.c_str(),
		                                                 pThisObject -> ctpp_error.error_code,
		                                                 pThisObject -> ctpp_error.ip);
		RETURN_FALSE;
	}

#ifdef CTPP_DEBUG
	/* Write data to output */
	M_CTPP::PHPOutputCollector oOutputCollector;
	std::string sTMP = pThisObject -> cdt -> RecursiveDump();
	((CTPP::OutputCollector *)(&oOutputCollector)) -> Collect("Data object", sizeof("Data object"));
	((CTPP::OutputCollector *)(&oOutputCollector)) -> Collect(sTMP.c_str(), sTMP.size());
#endif
}
/* }}} */

// Print data to specified buffer
/* {{{ proto string ctpp_output_string()
*/
PHP_FUNCTION(ctpp_output_string)
{
	using namespace CTPP;
	using namespace M_CTPP;

	CTPPVMObject        * pThisObject     = NULL;
	CTPPBytecodeObject  * pBytecodeObject = NULL;
	zval                * pRParam;
	zval                * pThis = getThis();
	if (pThis != NULL)
	{
		if(ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
		ctpp_object * obj = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);

		pThisObject = (CTPPVMObject *)(obj -> u.data);

		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"r", &pRParam) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE2(pBytecodeObject, CTPPBytecodeObject *, &pRParam, -1, (char *)"CTPP_BP", le_ctpp_bytecode_perm, le_ctpp_bytecode);
	}
	else
	{
		if(ZEND_NUM_ARGS() != 2) { WRONG_PARAM_COUNT; }
		zval * pRThis;
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"rr", &pRThis, &pRParam) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE(pThisObject, CTPPVMObject *, &pRThis, -1, (char *)"CTPP", le_ctpp);
		ZEND_FETCH_RESOURCE2(pBytecodeObject, CTPPBytecodeObject *, &pRParam, -1, (char *)"CTPP_BP", le_ctpp_bytecode_perm, le_ctpp_bytecode);
	}

	STLW::string sResponse;
	INT_32      iOK = -1;
	UINT_32     iIP = 0;
	/* Run virtual machine */
	try
	{
		/* Logger */
		PHPLogger oLogger;
		/* Output data collector */
		StringOutputCollector oOutputCollector(sResponse);
		/* Get program core */
		const VMMemoryCore * pVMMemoryCore = pBytecodeObject -> memory_core;

		/* Run program */
		VM oVM(  pThisObject -> syscall_factory,
		         pThisObject -> arg_stack_size,
		         pThisObject -> code_stack_size,
		         pThisObject -> steps_limit,
		         pThisObject -> debug_level);

		oVM.Init(pVMMemoryCore, &oOutputCollector, &oLogger);
		oVM.Run(pVMMemoryCore, &oOutputCollector, iIP, *(pThisObject -> cdt), &oLogger);
		iOK = 0;
	}
	catch (ZeroDivision           & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_ZERO_DIVISION_ERROR,           VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (ExecutionLimitReached  & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_EXECUTION_LIMIT_REACHED_ERROR, VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (CodeSegmentOverrun     & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_CODE_SEGMENT_OVERRUN_ERROR,    VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (InvalidSyscall         & e)
	{
		if (e.GetIP() != 0)
		{
			pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_INVALID_SYSCALL_ERROR,         VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP());
		}
		else
		{
			pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), std::string("Unsupported syscall: \"") + e.what() + "\"", CTPP_VM_ERROR | CTPP_INVALID_SYSCALL_ERROR,         VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP());
		}
	}
	catch (IllegalOpcode          & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_ILLEGAL_OPCODE_ERROR,          VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (StackOverflow          & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_STACK_OVERFLOW_ERROR,          VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (StackUnderflow         & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_STACK_UNDERFLOW_ERROR,         VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (VMException            & e) { pThisObject -> ctpp_error = CTPPError(e.GetSourceName(), e.what(), CTPP_VM_ERROR | CTPP_VM_GENERIC_ERROR,              VMDebugInfo(e.GetDebugInfo()).GetLine(), VMDebugInfo(e.GetDebugInfo()).GetLinePos(), e.GetIP()); }
	catch (CTPPUnixException      & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | CTPP_UNIX_ERROR,                    0, 0, iIP); }
	catch (CDTRangeException      & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | CTPP_RANGE_ERROR,                   0, 0, iIP); }
	catch (CDTAccessException     & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | CTPP_ACCESS_ERROR,                  0, 0, iIP); }
	catch (CDTTypeCastException   & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | CTPP_TYPE_CAST_ERROR,               0, 0, iIP); }
	catch (CTPPLogicError         & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | CTPP_LOGIC_ERROR,                   0, 0, iIP); }
	catch (CTPPException          & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | CTPP_UNKNOWN_ERROR,                 0, 0, iIP); }
	catch (STLW::exception        & e) { pThisObject -> ctpp_error = CTPPError("", e.what(), CTPP_VM_ERROR | STL_UNKNOWN_ERROR,                  0, 0, iIP); }
	catch (...)                        { pThisObject -> ctpp_error = CTPPError("", "Unknown Error", CTPP_VM_ERROR | STL_UNKNOWN_ERROR,           0, 0, iIP); }

	// Error occured
	if (iOK == -1)
	{
		php_error(E_WARNING, "%s(): %s (error code 0x%08X); IP: 0x%08X", get_active_function_name(TSRMLS_C),
		                                                 pThisObject -> ctpp_error.error_descr.c_str(),
		                                                 pThisObject -> ctpp_error.error_code,
		                                                 pThisObject -> ctpp_error.ip);
		RETURN_FALSE;
	}

#ifdef CTPP_DEBUG
	/* Write data to output */
	M_CTPP::PHPOutputCollector oOutputCollector;
	const STLW::string sTMP = pThisObject -> cdt -> RecursiveDump();
	((CTPP::OutputCollector *)(&oOutputCollector)) -> Collect("Data object", sizeof("Data object"));
	((CTPP::OutputCollector *)(&oOutputCollector)) -> Collect(sTMP.c_str(), sTMP.size());
#endif

RETURN_STRINGL((char *)sResponse.data(), sResponse.length(), 1);
}
/* }}} */

/* Dump parameters to standard output */
/* {{{ proto ctpp_dump_params()
*/
PHP_FUNCTION(ctpp_dump_params)
{
	CTPPVMObject * pThisObject = NULL;

	zval * pThis = getThis();
	if (pThis != NULL)
	{
		if(ZEND_NUM_ARGS() != 0) { WRONG_PARAM_COUNT; }
		ctpp_object * obj = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);

		pThisObject = (CTPPVMObject *)(obj -> u.data);
	}
	else
	{
		if(ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
		zval * pRThis;
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"r", &pRThis) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE(pThisObject, CTPPVMObject *, &pRThis, -1, (char *)"CTPP", le_ctpp);
	}

	try
	{
		/* Write data to output */
		M_CTPP::PHPOutputCollector oOutputCollector;
		const STLW::string sTMP = pThisObject -> cdt -> RecursiveDump();
		oOutputCollector.Collect(sTMP.c_str(), sTMP.size());
		RETURN_TRUE;
	}
	catch(...)
	{
		RETURN_FALSE;
	}
}

/* Dump parameters to string */
/* {{{ proto ctpp_dump_params_string()
*/
PHP_FUNCTION(ctpp_dump_params_string)
{
	CTPPVMObject * pThisObject = NULL;

	zval * pThis = getThis();
	if (pThis != NULL)
	{
		if(ZEND_NUM_ARGS() != 0) { WRONG_PARAM_COUNT; }
		ctpp_object * obj = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);

		pThisObject = (CTPPVMObject *)(obj -> u.data);
	}
	else
	{
		if(ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
		zval * pRThis;
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"r", &pRThis) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE(pThisObject, CTPPVMObject *, &pRThis, -1, (char *)"CTPP", le_ctpp);
	}

	try
	{
		/* Write data to string */
		const STLW::string sTMP = pThisObject -> cdt -> RecursiveDump();
		RETURN_STRINGL((char *)sTMP.data(), sTMP.length(), 1);
	}
	catch (...)
	{
		RETURN_FALSE;
	}
}

/* Dump parameters to string */
/* {{{ proto ctpp_set_steps_limit()
*/
PHP_FUNCTION(ctpp_set_steps_limit)
{
	CTPPVMObject  * pThisObject = NULL;
	long            lStepsLimit;

	zval * pThis = getThis();
	if (pThis != NULL)
	{
		if(ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
		ctpp_object * obj = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);

		pThisObject = (CTPPVMObject *)(obj -> u.data);

		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"l", &lStepsLimit) == FAILURE) { RETURN_FALSE; }
	}
	else
	{
		if(ZEND_NUM_ARGS() != 2) { WRONG_PARAM_COUNT; }
		zval * pRThis;
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"rl", &pRThis, &lStepsLimit) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE(pThisObject, CTPPVMObject *, &pRThis, -1, (char *)"CTPP", le_ctpp);
	}

	pThisObject -> steps_limit = lStepsLimit;

RETURN_TRUE;
}

/* Get last error */
/* {{{ proto ctpp_get_last_error()
*/
PHP_FUNCTION(ctpp_get_last_error)
{
	CTPPVMObject  * pThisObject = NULL;

	zval * pThis = getThis();
	if (pThis != NULL)
	{
		if(ZEND_NUM_ARGS() != 0) { WRONG_PARAM_COUNT; }
		ctpp_object * obj = (ctpp_object *) zend_object_store_get_object(pThis TSRMLS_CC);

		pThisObject = (CTPPVMObject *)(obj -> u.data);
	}
	else
	{
		if(ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
		zval * pRThis;

		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char *)"r", &pRThis) == FAILURE) { RETURN_FALSE; }
		ZEND_FETCH_RESOURCE(pThisObject, CTPPVMObject *, &pRThis, -1, (char *)"CTPP", le_ctpp);
	}

	char * szErrorDescr = estrdup(pThisObject -> ctpp_error.error_descr.c_str());
	char * szTemplateName = estrdup(pThisObject -> ctpp_error.template_name.c_str());

	/* Create a PHP array with error description */
	array_init(return_value);

	add_assoc_string(return_value, "template_name", szTemplateName, 1);
	add_assoc_long(return_value,   "line",          long(pThisObject -> ctpp_error.line));
	add_assoc_long(return_value,   "pos",           long(pThisObject -> ctpp_error.pos));
	add_assoc_long(return_value,   "ip",            long(pThisObject -> ctpp_error.ip));
	add_assoc_long(return_value,   "error_code",    pThisObject -> ctpp_error.error_code);
	add_assoc_string(return_value, "error_str",     szErrorDescr, 1);
}

/* Create bytecode object from template */
/* {{{ _php_create_bytecode_object2
*/
static CTPPBytecodeObject * _php_create_bytecode_object2(CTPP::VMExecutable * pICore, UINT_32 iSize, INT_32 iPersistent)
{
	TSRMLS_FETCH();

	using namespace CTPP;

	CTPPBytecodeObject * pCTPPBytecodeObject = NULL;

	if (iPersistent == BYTECODE_PERM_OBJECT) { pCTPPBytecodeObject = (CTPPBytecodeObject *)malloc(sizeof(CTPPBytecodeObject));  }
	else                                     { pCTPPBytecodeObject = (CTPPBytecodeObject *)emalloc(sizeof(CTPPBytecodeObject)); }

	pCTPPBytecodeObject -> resource_type = iPersistent;
	pCTPPBytecodeObject -> core          = NULL;
	pCTPPBytecodeObject -> core_size     = iSize;
	pCTPPBytecodeObject -> memory_core   = NULL;

	VMExecutable * pCore = NULL;
	/* Try to allocate memory, if failed, it is safe of memory leaks */
	if (iPersistent == BYTECODE_PERM_OBJECT) { pCore = (VMExecutable *)malloc(iSize);  }
	else                                     { pCore = (VMExecutable *)emalloc(iSize); }

	memcpy(pCore, pICore, iSize);
	efree(pICore);

	/* Okay, allocate memory for VMMemoryCore */
	VMMemoryCore * pVMMemoryCore = NULL;

	if (iPersistent == BYTECODE_PERM_OBJECT) { pVMMemoryCore = (VMMemoryCore *)malloc(sizeof(VMMemoryCore));  }
	else                                     { pVMMemoryCore = (VMMemoryCore *)emalloc(sizeof(VMMemoryCore)); }

	/* Placement new */
	try
	{
		::new(pVMMemoryCore) VMMemoryCore(pCore);
	}
	catch(...)
	{
		if (iPersistent == BYTECODE_PERM_OBJECT) { free(pCTPPBytecodeObject);  free(pCore);  free(pVMMemoryCore);  efree(pICore); }
		else                                     { efree(pCTPPBytecodeObject); efree(pCore); efree(pVMMemoryCore); efree(pICore); }

		php_error(E_WARNING, "%s(): Impossible happened in ::new(pVMMemoryCore) VMMemoryCore(oCore); at `" __FILE__ "`, line %d", get_active_function_name(TSRMLS_C), __LINE__);
		return NULL;
	}

	/* All done */
	pCTPPBytecodeObject -> core        = pCore;
	pCTPPBytecodeObject -> core_size   = iSize;
	pCTPPBytecodeObject -> memory_core = pVMMemoryCore;

return pCTPPBytecodeObject;
}

/* Create bytecode object */
/* {{{ _php_create_bytecode_object
*/
static CTPPBytecodeObject * _php_create_bytecode_object(CCHAR_P szBytecode, INT_32 iPersistent)
{
	TSRMLS_FETCH();

	using namespace CTPP;

	CTPPBytecodeObject * pCTPPBytecodeObject = NULL;

	if (iPersistent == BYTECODE_PERM_OBJECT) { pCTPPBytecodeObject = (CTPPBytecodeObject *)malloc(sizeof(CTPPBytecodeObject));  }
	else                                     { pCTPPBytecodeObject = (CTPPBytecodeObject *)emalloc(sizeof(CTPPBytecodeObject)); }

	pCTPPBytecodeObject -> resource_type = iPersistent;
	pCTPPBytecodeObject -> core          = NULL;
	pCTPPBytecodeObject -> core_size     = 0;
	pCTPPBytecodeObject -> memory_core   = NULL;

	/* Load file */

	/* Get file size */
	struct stat oStat;
	if (stat(szBytecode, &oStat) == -1)
	{
		php_error(E_WARNING, "%s(): stat(\"%s\") failed: %s (%d)", get_active_function_name(TSRMLS_C), szBytecode, strerror(errno), errno);
		return NULL;
	}

	if (oStat.st_size == 0)
	{
		php_error(E_WARNING, "%s(): stat(\"%s\") failed: file size == 0", get_active_function_name(TSRMLS_C), szBytecode);
		return NULL;
	}

	VMExecutable * pCore = NULL;
	/* Try to allocate memory, if failed, it is safe of memory leaks */
	if (iPersistent == BYTECODE_PERM_OBJECT) { pCore = (VMExecutable *)malloc(oStat.st_size);  }
	else                                     { pCore = (VMExecutable *)emalloc(oStat.st_size); }

	/* Load file */
	FILE * F = fopen(szBytecode, "r");
	if (F == NULL)
	{
		if (iPersistent == BYTECODE_PERM_OBJECT) { free(pCTPPBytecodeObject);  free(pCore);  }
		else                                     { efree(pCTPPBytecodeObject); efree(pCore); }

		php_error(E_WARNING, "%s(): fopen(\"%s\", \"r\") failed: %s (%d)", get_active_function_name(TSRMLS_C), szBytecode, strerror(errno), errno);
		return NULL;
	}

	/* Read from file */
	if (fread((void *)pCore, oStat.st_size, 1, F) != 1)
	{
		if (iPersistent == BYTECODE_PERM_OBJECT) { free(pCTPPBytecodeObject);  free(pCore);  }
		else                                     { efree(pCTPPBytecodeObject); efree(pCore); }
		fclose(F);

		php_error(E_WARNING, "%s(): fread(\"%s\", oStat.st_size=%u) failed: %s (%d)", get_active_function_name(TSRMLS_C), szBytecode, (unsigned int)oStat.st_size, strerror(errno), errno);
		return NULL;
	}

	/* All Done */
	fclose(F);

	if (!(pCore -> magic[0] == 'C' &&
	      pCore -> magic[1] == 'T' &&
	      pCore -> magic[2] == 'P' &&
	      pCore -> magic[3] == 'P'))
	{
		if (iPersistent == BYTECODE_PERM_OBJECT) { free(pCTPPBytecodeObject);  free(pCore);  }
		else                                     { efree(pCTPPBytecodeObject); efree(pCore); }

		php_error(E_WARNING, "%s(): \"%s\" IS NOT CTPP executable, invalid magick number", get_active_function_name(TSRMLS_C), szBytecode);
		return NULL;
	}

	/* Okay, allocate memory for VMMemoryCore */
	VMMemoryCore * pVMMemoryCore = NULL;

	if (iPersistent == BYTECODE_PERM_OBJECT) { pVMMemoryCore = (VMMemoryCore *)malloc(sizeof(VMMemoryCore));  }
	else                                     { pVMMemoryCore = (VMMemoryCore *)emalloc(sizeof(VMMemoryCore)); }

	/* Placement new */
	try
	{
		::new(pVMMemoryCore) VMMemoryCore(pCore);
	}
	catch(...)
	{
		if (iPersistent == BYTECODE_PERM_OBJECT) { free(pCTPPBytecodeObject);  free(pCore);  free(pVMMemoryCore);  }
		else                                     { efree(pCTPPBytecodeObject); efree(pCore); efree(pVMMemoryCore); }

		php_error(E_WARNING, "%s(): Impossible happened in ::new(pVMMemoryCore) VMMemoryCore(oCore); at `" __FILE__ "`, line %d", get_active_function_name(TSRMLS_C), __LINE__);
		return NULL;
	}

	/* All done */
	pCTPPBytecodeObject -> core        = pCore;
	pCTPPBytecodeObject -> core_size   = oStat.st_size;
	pCTPPBytecodeObject -> memory_core = pVMMemoryCore;

return pCTPPBytecodeObject;
}
/* }}} */

/* Dump array element */
/* {{{ _php_array_element_dump
*/
static int _php_array_element_dump(zval ** pDataSet TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
{
	using namespace CTPP;

	CHAR_8      szNumKey[1024 + 1];
	CDT       * pCDT     = (CTPP::CDT *)va_arg(args, void *);
	uint        uiKeyLen = 0;
	CDT         oTMP;

	/* Import data */
	_php_import_value(pDataSet, &oTMP);

	/* Key of PHP array is numeric */
	if (hash_key -> nKeyLength == 0)
	{
		/* ARRAY */
		const CDT::eValType eType = pCDT -> GetType();
		if (eType == CDT::ARRAY_VAL || eType == CDT::UNDEF)
		{
			/* Push element */
			pCDT -> operator[](pCDT -> Size()) = oTMP;
		}
		/* HASH */
		else /* if (pCDT -> GetType() == CDT::HASH_VAL) */
		{
			/* Map long int to char key */
			uiKeyLen = snprintf(szNumKey, 1024, "%ld", hash_key -> h);
			pCDT -> operator[](STLW::string(szNumKey, uiKeyLen)) = oTMP;
		}
	}
	/* Key of PHP array is string */
	else
	{
		/* ARRAY */
		const CDT::eValType eType = pCDT -> GetType();
		if (eType == CDT::ARRAY_VAL || eType == CDT::UNDEF)
		{
			/* Convert from ARRAY to HASH */
			CDT oTMPArray;
			const UINT_32 iSize = pCDT -> Size();
			for (UINT_32 iPos = 0; iPos < iSize; ++iPos)
			{
				uiKeyLen = snprintf(szNumKey, 1024, "%d", iPos);
				oTMPArray[STLW::string(szNumKey, uiKeyLen)] = pCDT -> operator[](iPos);
			}

			pCDT -> operator=(oTMPArray);
		}

		pCDT -> operator[](STLW::string(hash_key -> arKey, hash_key -> nKeyLength - 1)) = oTMP;
	}

return 0;
}

/* Add parameters */
/* {{{ _php_import_value
*/
static INT_32 _php_import_value(zval ** pDataSet, CTPP::CDT * pCDT)
{
	TSRMLS_FETCH();

	try
	{
		switch(Z_TYPE_PP(pDataSet))
		{
			/* Simple data types */
			case IS_LONG:
				pCDT -> operator=(INT_64(Z_LVAL_PP(pDataSet)));
				break;
			case IS_DOUBLE:
				pCDT -> operator=(W_FLOAT(Z_DVAL_PP(pDataSet)));
				break;
			case IS_STRING:
				pCDT -> operator=(STLW::string(Z_STRVAL_PP(pDataSet), Z_STRLEN_PP(pDataSet)));
				break;
			case IS_BOOL:
				if (Z_LVAL_PP(pDataSet)) { pCDT -> operator=(INT_64(Z_LVAL_PP(pDataSet))); }
				break;
			/* Nothing to do with NULL */
			case IS_NULL:
				break;
			/* Constant */
			case IS_CONSTANT:
				pCDT -> operator=("*CONSTANT*");
				break;
			/* Unsupported data types */
			case IS_OBJECT:
				pCDT -> operator=("*OBJECT*");
				break;
			/* Opaque resource */
			case IS_RESOURCE:
				pCDT -> operator=("*RESOURCE*");
				break;
			/* Complex data types */
			case IS_ARRAY:
			case IS_CONSTANT_ARRAY:
				{
					HashTable * pHashTable = Z_ARRVAL_PP(pDataSet);
					if (pHashTable -> nApplyCount > 1)
					{
						pCDT -> operator=("*RECURSION*");
						return IS_NULL;
					}

					zend_hash_apply_with_arguments(pHashTable TSRMLS_CC, (apply_func_args_t)_php_array_element_dump, 1, pCDT);
				}
				return IS_ARRAY;

			/* This should *not* happened */
			default:
				return -1;
		}
	}
	catch(...)
	{
		return -1;
	}

/* All done */
return 0;
}
/* }}} */

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
